package snafu.framework.agents;

import java.util.Set;

import snafu.framework.actions.AbstractAction;
import snafu.framework.actions.AbstractReaction;
import snafu.framework.objects.WorldObject;
import snafu.framework.objects.WorldObjectItself;
import snafu.framework.senses.Sense;
import snafu.framework.senses.stimuli.Stimulus;
import snafu.framework.states.State;
import snafu.framework.states.StateItself;
import snafu.framework.view.Access;
import snafu.framework.worlds.WorldMap;
import snafu.utility.Cast;

public class AgentItself extends WorldObjectItself<AgentView> implements Agent {

	/** The brain controlling the logic. */
	protected AgentBrain brain;
	/** The agent's state. */
	protected AgentStateItself state;

	/**
	 * Initiates a new agent with the given brain. Will set the initial state of the agent to the
	 * initial state of the brain if it has any. Else it will be an empty state.
	 * 
	 * @param brain
	 *            the brain that controlls the agent
	 */
	public AgentItself(AgentBrain brain) {
		this.brain = brain;
		this.state = (AgentStateItself) getInitialState();
	}
	
	@Override
	public AbstractAction act(WorldMap perception) {
		AbstractAction action = brain.act(perception, Cast.cast(state.createView(Access.PRIVATE), AgentState.class));
		if (action == null) {
			return null;
		}
		
		action.setAgent(this);
		return action;
	}
	
	@Override
	public AgentView createView(Access access) {
		return new AgentView(access, this);
	}
	
	@Override
	public State getState() {
		return state;
	}
	
	@Override
	public boolean canPercept(WorldObject object) {
		Set<Stimulus> stimuli = object.getState().getStimuli();
		for (Sense sense : state.getSenses()) {
			for (Stimulus stimulus : stimuli) {
				if (sense.canPercept(stimulus)) {
					return  true;
				}
			}
		}
		return false;
	}
	
	@Override
	public Class<? extends AgentBrain> getBrain() {
		return brain == null ? null :brain.getClass();
	}
	
	@Override
	public StateItself getInitialState() {
		return brain == null ? new AgentStateItself() : brain.getInitialState();
	}

	@Override
	public String output() {
		return brain == null ? "brainless agent" : brain.output();
	}
	
	@Override
	public AbstractReaction react(AbstractAction action) {
		return brain == null ? null : brain.react(action);
	}
}
